package nic

import (
    "unsafe"
    "go-os-qemu/pkg/pci"
    "go-os-qemu/pkg/vga"
)

const (
    vendorIntel   = 0x8086
    deviceE1000   = 0x100E // QEMU e1000
    bar0Offset    = 0x10
    regSTATUS     = 0x0008
)

type E1000 struct {
    Bus   uint8
    Dev   uint8
    Func  uint8
    MMIO  uint32 // BAR0 base (memory mapped)
}

// FindE1000 scans PCI devices to locate an Intel e1000 NIC.
func FindE1000() (*E1000, bool) {
    devs := pci.Enumerate()
    for _, d := range devs {
        if d.Vendor == vendorIntel && d.Device == deviceE1000 {
            bar0 := pci.Read32(d.Bus, d.Dev, d.Func, bar0Offset)
            // Mask off lower attribute bits (memory BAR)
            base := bar0 &^ 0xF
            return &E1000{Bus: d.Bus, Dev: d.Dev, Func: d.Func, MMIO: base}, true
        }
    }
    return nil, false
}

// ReadStatus reads the STATUS register via MMIO to verify device accessibility.
func (e *E1000) ReadStatus() uint32 {
    ptr := unsafe.Pointer(uintptr(e.MMIO) + uintptr(regSTATUS))
    return *(*uint32)(ptr)
}

// MMIO helpers
func (e *E1000) mmioWrite32(off uint32, val uint32) {
    ptr := (*uint32)(unsafe.Pointer(uintptr(e.MMIO) + uintptr(off)))
    *ptr = val
}
func (e *E1000) mmioRead32(off uint32) uint32 {
    return *(*uint32)(unsafe.Pointer(uintptr(e.MMIO) + uintptr(off)))
}

// Registers (subset)
const (
    REG_TCTL  = 0x00400
    REG_TIPG  = 0x00410
    REG_TDBAL = 0x03800
    REG_TDBAH = 0x03804
    REG_TDLEN = 0x03808
    REG_TDH   = 0x03810
    REG_TDT   = 0x03818

    REG_RCTL  = 0x00100
    REG_RDBAL = 0x02800
    REG_RDBAH = 0x02804
    REG_RDLEN = 0x02808
    REG_RDH   = 0x02810
    REG_RDT   = 0x02818

    REG_RAL0  = 0x05400
    REG_RAH0  = 0x05404
)

// TCTL bits
const (
    TCTL_EN  = 1 << 1
    TCTL_PSP = 1 << 3
    TCTL_CT_SHIFT   = 4
    TCTL_COLD_SHIFT = 12
)

// RCTL bits (partial)
const (
    RCTL_EN  = 1 << 1
    RCTL_UPE = 1 << 3
    RCTL_MPE = 1 << 4
    RCTL_BAM = 1 << 9
)

// Descriptor definitions
type txDesc struct {
    addrLo uint32
    addrHi uint32
    length uint16
    cso    uint8
    cmd    uint8
    status uint8
    css    uint8
    special uint16
}

type rxDesc struct {
    addrLo uint32
    addrHi uint32
    length uint16
    csum   uint16
    status uint8
    errors uint8
    special uint16
}

const (
    ringSize   = 16
    rxBufSize  = 2048
)

var (
    txRing [ringSize]txDesc
    rxRing [ringSize]rxDesc
    rxBufs [ringSize][rxBufSize]byte
    txBufs [ringSize][2048]byte
    RxCount uint32
    TxCount uint32
)

// Optional RX handler callback to avoid import cycles.
var RxHandler func([]byte)

func phys(ptr unsafe.Pointer) uint32 { return uint32(uintptr(ptr)) }

// Init sets up TX/RX rings and enables the device
func (e *E1000) Init() bool {
    // Program TX ring base/len
    e.mmioWrite32(REG_TDBAL, phys(unsafe.Pointer(&txRing[0])))
    e.mmioWrite32(REG_TDBAH, 0)
    e.mmioWrite32(REG_TDLEN, uint32(ringSize*int(unsafe.Sizeof(txDesc{}))))
    e.mmioWrite32(REG_TDH, 0)
    e.mmioWrite32(REG_TDT, 0)

    // Program RX ring base/len and buffers
    e.mmioWrite32(REG_RDBAL, phys(unsafe.Pointer(&rxRing[0])))
    e.mmioWrite32(REG_RDBAH, 0)
    e.mmioWrite32(REG_RDLEN, uint32(ringSize*int(unsafe.Sizeof(rxDesc{}))))
    e.mmioWrite32(REG_RDH, 0)
    e.mmioWrite32(REG_RDT, ringSize-1)
    for i := 0; i < ringSize; i++ {
        rxRing[i].addrLo = phys(unsafe.Pointer(&rxBufs[i][0]))
        rxRing[i].addrHi = 0
        rxRing[i].status = 0
    }

    // Configure transmit: enable, set collision threshold and cool-down
    tctl := uint32(TCTL_EN | TCTL_PSP) |
        (uint32(0x10) << TCTL_CT_SHIFT) |
        (uint32(0x40) << TCTL_COLD_SHIFT)
    e.mmioWrite32(REG_TCTL, tctl)
    // Set inter-packet gap typical values
    e.mmioWrite32(REG_TIPG, 0x0060200A)

    // Enable receive: accept broadcast and (optionally) all packets
    rctl := uint32(RCTL_EN | RCTL_BAM)
    e.mmioWrite32(REG_RCTL, rctl)

    // Read and show MAC for confirmation
    macLo := e.mmioRead32(REG_RAL0)
    macHi := e.mmioRead32(REG_RAH0)
    mac := [6]byte{
        byte(macLo & 0xFF), byte((macLo >> 8) & 0xFF), byte((macLo >> 16) & 0xFF), byte((macLo >> 24) & 0xFF),
        byte(macHi & 0xFF), byte((macHi >> 8) & 0xFF),
    }
    vga.SetColor(vga.ColorLightGray, vga.ColorBlack)
    vga.Print("e1000 MAC: ")
    for i := 0; i < 6; i++ {
        if i != 0 { vga.Print(":") }
        printHexByte(mac[i])
    }
    vga.Println("")

    return true
}

// GetMAC returns the NIC's MAC address.
func (e *E1000) GetMAC() [6]byte {
    macLo := e.mmioRead32(REG_RAL0)
    macHi := e.mmioRead32(REG_RAH0)
    return [6]byte{
        byte(macLo & 0xFF), byte((macLo >> 8) & 0xFF), byte((macLo >> 16) & 0xFF), byte((macLo >> 24) & 0xFF),
        byte(macHi & 0xFF), byte((macHi >> 8) & 0xFF),
    }
}

// SendFrame transmits a raw Ethernet frame (must be >= 60 bytes)
func (e *E1000) SendFrame(b []byte) bool {
    if len(b) < 60 { return false }
    // Get tail
    tdt := e.mmioRead32(REG_TDT)
    idx := int(tdt) % ringSize
    // Copy into ring-owned persistent buffer
    for i := 0; i < len(b); i++ { txBufs[idx][i] = b[i] }
    txRing[idx].addrLo = phys(unsafe.Pointer(&txBufs[idx][0]))
    txRing[idx].addrHi = 0
    txRing[idx].length = uint16(len(b))
    // CMD: EOP(1<<0) | IFCS(1<<1) | RS(1<<3)
    txRing[idx].cmd = 0x0B
    txRing[idx].status = 0
    e.mmioWrite32(REG_TDT, uint32((idx+1) % ringSize))
    TxCount++
    return true
}

// RX status bits
const (
    RX_STAT_DD = 1 << 0
)

// DumpRx polls RX ring and prints up to max frames summary.
func (e *E1000) DumpRx(max int) int {
    count := 0
    for i := 0; i < ringSize && count < max; i++ {
        if (rxRing[i].status & RX_STAT_DD) != 0 {
            ln := int(rxRing[i].length)
            vga.SetColor(vga.ColorLightGray, vga.ColorBlack)
            vga.Print("RX ")
            printDec(ln)
            vga.Print(" bytes: ")
            // print first 16 bytes
            lim := 16
            if ln < lim { lim = ln }
            for j := 0; j < lim; j++ {
                if j != 0 { vga.Print(" ") }
                printHexByte(rxBufs[i][j])
            }
            vga.Println("")
            // Pass to registered RX handler (if any)
            if RxHandler != nil { RxHandler(rxBufs[i][:ln]) }
            rxRing[i].status = 0
            e.mmioWrite32(REG_RDT, uint32(i))
            RxCount++
            count++
        }
    }
    return count
}

// Local decimal printing to avoid cross-package linkname issues.
func printDec(n int) {
    if n == 0 {
        vga.Print("0")
        return
    }
    var buf [16]byte
    i := len(buf)
    for n > 0 && i > 0 {
        i--
        buf[i] = byte('0' + (n % 10))
        n /= 10
    }
    for ; i < len(buf); i++ {
        vga.Print(string(rune(buf[i])))
    }
}

// Local hex printing helpers to avoid cross-package linkname.
func printHexNibble(n uint8) {
    d := n & 0x0F
    if d < 10 {
        vga.Print(string(rune('0' + d)))
    } else {
        vga.Print(string(rune('A' + (d - 10))))
    }
}

func printHexByte(b byte) {
    printHexNibble(uint8(b >> 4))
    printHexNibble(uint8(b & 0x0F))
}